package com.bookmall.cart.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bookmall.cart.domain.CartItem;
import com.bookmall.cart.domain.OrderInfo;
import com.bookmall.cart.domain.OrderInfoItem;
import com.bookmall.cart.domain.OrderPaymentInfo;
import com.bookmall.cart.domain.vo.*;
import com.bookmall.cart.feign.MemberFeignService;
import com.bookmall.cart.feign.ProductFeignService;
import com.bookmall.cart.interceptor.CartInterceptor;
import com.bookmall.cart.mapper.OrderInfoItemMapper;
import com.bookmall.cart.mapper.OrderInfoMapper;
import com.bookmall.cart.service.IOrderInfoItemService;
import com.bookmall.cart.service.IOrderInfoService;
import com.bookmall.cart.service.IOrderPaymentInfoService;
import com.bookmall.cart.web.CartController;
import com.bookmall.cart.web.OrderController;
import com.bookmall.common.constant.CartConstant;
import com.bookmall.common.constant.ExceptionTypeConstants;
import com.bookmall.common.constant.MyStatusConstants;
import com.bookmall.common.entity.to.MemberInfoTo;
import com.bookmall.common.enume.OrderStatusEnum;
import com.ruoyi.common.core.exception.ServiceException;
import com.ruoyi.common.core.utils.DateUtils;
import com.ruoyi.common.core.utils.IdUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Author 胡菜鸡
 * @Create 2022-04-21-10:22
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements IOrderInfoService {
    /** 页面传递来的订单数据 */
    private ThreadLocal<OrderSubmitVo> orderThreadLocal = new ThreadLocal<>();

    @Autowired
    private MemberFeignService memberFeignService;

    @Autowired
    private CartController cartController;

    @Autowired
    private ThreadPoolExecutor executor;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private IOrderPaymentInfoService orderPaymentInfoService;

    @Autowired
    private IOrderInfoItemService orderInfoItemService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private IOrderPaymentInfoService paymentInfoService;

    @Autowired
    private IOrderInfoService orderInfoService;

    /**
     * 获取订单确认页需要的数据
     * @author 胡菜鸡
     * @date 2022/4/21 17:26
     */
    @Override
    public OrderInfoVo getOrder() throws ExecutionException, InterruptedException {
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        MemberInfoTo memberInfoTo = CartInterceptor.threadLocal.get();
        CompletableFuture<Void> addressInfo = CompletableFuture.runAsync(() -> {
            //1、远程获取会员的所有收获地址列表
            List<MemberAddressVo> address = memberFeignService.getAddress(memberInfoTo.getId());
            orderInfoVo.setAddress(address);
        }, executor);

        CompletableFuture<Void> cartInfo = CompletableFuture.runAsync(() -> {
            //2、查询购物车所有选中的购物项
            CartInterceptor.threadLocal.set(memberInfoTo);
            List<CartItem> currentUserCartItems = cartController.getCurrentUserCartItems();
            orderInfoVo.setItems(currentUserCartItems);
        }, executor);

        // 3、获取会员优惠信息——积分
        orderInfoVo.setIntegration(memberInfoTo.getIntegration());
        CompletableFuture.allOf(addressInfo, cartInfo).get();

        // TODO 4、获取优惠券
        //5、其他数据自动计算
        //TODO 6、防止重复令牌
        String token = IdUtils.simpleUUID();
        //服务器存一份
        redisTemplate.opsForValue().set(CartConstant.USER_ORDER_TOKEN_PREFIX + memberInfoTo.getId(), token, 30, TimeUnit.MINUTES);
        //页面存一份
        orderInfoVo.setOrderToken(token);

        return orderInfoVo;
    }

    /**
     * 提交订单
     * @author 胡菜鸡
     * @date 2022/4/24 21:09
     */
    @GlobalTransactional
    @Transactional
    @Override
    public OrderResponseVo submitOrder(OrderSubmitVo orderSubmitVo) throws ServiceException {
        if(StringUtils.isBlank(orderSubmitVo.getAddressId())){
            throw new ServiceException("没有提交订单地址");
        }
        OrderResponseVo orderResponseVo = new OrderResponseVo();
        orderThreadLocal.set(orderSubmitVo);
        MemberInfoTo memberInfoTo = CartInterceptor.threadLocal.get();

        //1、验证令牌（令牌的对比和删除必须保证原子性）
        // 原子性脚本（0失败，1成功）
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        String token = orderSubmitVo.getOrderToken();
        // 执行原子性脚本
        Long result = redisTemplate.execute(
                new DefaultRedisScript<Long>(script, Long.class),
                Arrays.asList(CartConstant.USER_ORDER_TOKEN_PREFIX + memberInfoTo.getId()),
                token);
        if(result == 0L){
            //令牌验证失败
            throw new ServiceException("订单已提交或订单已失效，请检查订单信息", ExceptionTypeConstants.ORDER_SUBMIT);
        }

        //2、创建一个订单、订单项等信息
        OrderCreateVo order = createOrder(orderSubmitVo);

        //3、保存订单和订单项
        if(!saveOrder(order)){
            throw new ServiceException("订单保存失败", ExceptionTypeConstants.ORDER_SAVE);
        }

        //4、库存锁定
        WareBookLockVo wareBookLockVo = new WareBookLockVo();
        wareBookLockVo.setOrderSn(order.getOrder().getOrderSn());   //订单号
        //映射出库存锁定对象
        List<WareBookLockVo.OrderItemsLock> locks = order.getItems().stream().map(item -> {
            WareBookLockVo.OrderItemsLock orderItemsLock = new WareBookLockVo.OrderItemsLock();
            orderItemsLock.setId(item.getBookId());
            orderItemsLock.setCount(item.getBookCount());
            orderItemsLock.setBookName(item.getBookName());
            return orderItemsLock;
        }).collect(Collectors.toList());
        wareBookLockVo.setLocks(locks);

        //远程锁库存
        AjaxResult ajaxResult = productFeignService.orderLockStock(wareBookLockVo);
        if(ajaxResult.get(AjaxResult.CODE_TAG).equals(200)){
            //锁定成功
            orderResponseVo.setOrder(order.getOrder());
            orderResponseVo.setOrderItem(order.getItems());
            //订单创建成功，向mq发送消息
            rabbitTemplate.convertAndSend("order-event-exchange", "order.create.order", orderResponseVo);
            return orderResponseVo;
        } else {
            //锁定失败
            throw new ServiceException("库存不足", ExceptionTypeConstants.PRODUCT_WARE_NO_STOCK);
        }
    }

    /**
     * 订单已过期，关闭订单
     * @author 胡菜鸡
     * @date 2022/4/26 14:05
     */
    @Override
    @Transactional
    public void closeOrder(OrderResponseVo orderResponseVo) {
        //查询当前订单的状态
        OrderInfo order = this.getById(orderResponseVo.getOrder().getId());
        if(order != null && order.getStatus() == OrderStatusEnum.CREATE_NEW.getCode()){
            //订单过期都还没有支付，则关闭订单
            order.setStatus(OrderStatusEnum.CANCLED.getCode());
            this.updateById(order);

            //订单关闭后，将订单项锁定的库存返回
            List<BookWareInfoVo> bookInfoVoList = new ArrayList<>();
            List<OrderInfoItem> orderItemList = orderResponseVo.getOrderItem();
            for (OrderInfoItem orderItem : orderItemList) {
                BookWareInfoVo bookWareInfoVo = new BookWareInfoVo();
                bookWareInfoVo.setId(orderItem.getBookId());
                bookWareInfoVo.setCountStockLocked(Long.valueOf(orderItem.getBookCount()));
                bookInfoVoList.add(bookWareInfoVo);
            }
            AjaxResult ajaxResult = productFeignService.editLock(bookInfoVoList);
            if(!ajaxResult.get(AjaxResult.CODE_TAG).equals(200)){
                throw new ServiceException("订单关闭失败", ExceptionTypeConstants.ORDER_CLOSE_FAIL);
            }
        }
    }

    /**
     * 获取当前订单的支付信息
     * @author 胡菜鸡
     * @date 2022/4/26 16:15
     */
    @Override
    public PayVo getOrderPay(String orderSn) {
        PayVo payVo = new PayVo();
        //查找订单
        OrderInfo order = this.getOne(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getOrderSn, orderSn));
        //查找订单项
        List<OrderInfoItem> orderItems = orderInfoItemService.list(new LambdaQueryWrapper<OrderInfoItem>()
                .eq(OrderInfoItem::getOrderSn, orderSn));
        //设置支付金额
        BigDecimal payAmount = order.getPayAmount();
        payVo.setTotal_amount(payAmount.toString());
        //设置订单号
        payVo.setOut_trade_no(orderSn);
        //设置订单标题和备注
        OrderInfoItem item = orderItems.get(0);
        payVo.setSubject(item.getBookName() + "等书籍");
        payVo.setBody(order.getNote());
        return payVo;
    }

    /**
     * 根据会员id获取该会员所有订单及对应订单项信息
     * @author 胡菜鸡
     * @date 2022/4/27 15:52
     */
    @Override
    public OrderPageResVo getOrderByMemberId(String memberId, PersonParamVo personParamVo) {
        LambdaQueryWrapper<OrderInfo> orderWrapper = new LambdaQueryWrapper<>();
        //拼接查询条件
        if(personParamVo.getOrderSn() != null) orderWrapper.eq(OrderInfo::getOrderSn, personParamVo.getOrderSn());
        if(personParamVo.getStatus() != null && personParamVo.getStatus() != -1) orderWrapper.eq(OrderInfo::getStatus, personParamVo.getStatus());
        if(personParamVo.getCreateTime() != null){
            Calendar cal = Calendar.getInstance();
            Date date = DateUtils.getNowDate();
            cal.setTime(date);
            if(personParamVo.getCreateTime() > 0){
                cal.add(Calendar.MONTH, 0 - personParamVo.getCreateTime()); //获取当前时间的前 ? 个月
                Date newTime = cal.getTime();
                orderWrapper.gt(OrderInfo::getCreateTime, newTime);
            } else if(personParamVo.getCreateTime() < 0) {
                cal.add(Calendar.MONTH, personParamVo.getCreateTime()); //获取当前时间的前 ? 个月
                Date newTime = cal.getTime();
                orderWrapper.lt(OrderInfo::getCreateTime, newTime);
            }
        }
        //分页查询订单
        Page<OrderInfo> page = this.page(
                new Page<>(personParamVo.getPageNum(), personParamVo.getPageSize()),
                orderWrapper.eq(OrderInfo::getMemberId, memberId));
        //查询订单对应的购物项
        List<OrderResponseVo> collect = page.getRecords().stream().map(memberOrder -> {
            OrderResponseVo orderResponseVo = new OrderResponseVo();
            List<OrderInfoItem> items = orderInfoItemService.getOrderItems(memberOrder.getOrderSn());
            orderResponseVo.setOrder(memberOrder);
            orderResponseVo.setOrderItem(items);
            return orderResponseVo;
        }).collect(Collectors.toList());

        //拼接返回数据
        OrderPageResVo orderPageResVo = new OrderPageResVo();
        orderPageResVo.setOrderResponseVo(collect);
        orderPageResVo.setTotalPage(page.getPages());
        orderPageResVo.setTotalOrder(page.getTotal());
        orderPageResVo.setPageNum(Long.valueOf(personParamVo.getPageNum()));
        return orderPageResVo;
    }

    /**
     * 处理支付宝支付后的数据
     * @author 胡菜鸡
     * @date 2022/4/28 14:51
     * @param payAsyncVo 支付完成的异步VO
     * @return success 成功
     * @return fail 失败
     */
    @Override
    @Transactional
    public String handlePayResult(PayAsyncVo payAsyncVo) throws ServiceException {
        //1、保存交易流水
        OrderPaymentInfo paymentInfo = new OrderPaymentInfo();
        paymentInfo.setOrderSn(payAsyncVo.getOut_trade_no());
        paymentInfo.setAlipayTradeNo(payAsyncVo.getTrade_no());
        paymentInfo.setTotalAmount(new BigDecimal(payAsyncVo.getTotal_amount()));
        paymentInfo.setSubject(payAsyncVo.getSubject());
        paymentInfo.setPaymentStatus(payAsyncVo.getTrade_status());
        paymentInfo.setCallbackTime(payAsyncVo.getNotify_time());
        paymentInfo.setCreateTime(payAsyncVo.getGmt_create());
        boolean save = paymentInfoService.save(paymentInfo);
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        OrderInfo orderInfo = orderInfoService.getOne(wrapper.eq(OrderInfo::getOrderSn, paymentInfo.getOrderSn()));
        orderInfo.setPaymentTime(DateUtils.getNowDate());
        boolean update = orderInfoService.updateById(orderInfo);
        if(!save || !update){
            throw new ServiceException("支付流水信息保存失败", ExceptionTypeConstants.PAYMENT_INFO_SAVE_FAIL);
        }

        //2、修改订单状态信息
        if(payAsyncVo.getTrade_status().equals("TRADE_SUCCESS") || payAsyncVo.getTrade_status().equals("TRADE_FINISHED")){
            String orderSn = payAsyncVo.getOut_trade_no();
            boolean status = updateOrderStatus(orderSn, OrderStatusEnum.PAYED.getCode());
            if(!status){
                throw new ServiceException("支付状态修改失败", ExceptionTypeConstants.ORDER_UPDATE_STATUS_FAIL);
            }
        }

        //3、清空购物车中已购买的商品


        return "success";
    }

    /**
     * 根据订单号修改订单状态
     * @author 胡菜鸡
     * @date 2022/4/28 15:12
     */
    @Override
    public Boolean updateOrderStatus(String orderSn, Integer status) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setStatus(status);
        return update(orderInfo, wrapper.eq(OrderInfo::getOrderSn, orderSn));
    }

    /**
     * 保存订单和订单项数据
     * @author 胡菜鸡
     * @date 2022/4/24 23:55
     */
    private boolean saveOrder(OrderCreateVo order) {
        return this.save(order.getOrder()) && orderInfoItemService.saveBatch(order.getItems());
    }

    /**
     * 创建一个订单
     * @author 胡菜鸡
     * @date 2022/4/24 21:35
     */
    private OrderCreateVo createOrder(OrderSubmitVo orderSubmitVo)  throws ServiceException{
        OrderCreateVo orderCreateVo = new OrderCreateVo();
        //1、构建基本订单数据
        OrderInfo orderInfo = buildOrder();
        //2、构建订单项数据
        List<OrderInfoItem> orderInfoItems = buildOrderItems(orderInfo.getOrderSn());
        //3、计算订单价格相关
        orderInfo = computePrice(orderInfo, orderInfoItems, orderSubmitVo);

        orderCreateVo.setOrder(orderInfo);
        orderCreateVo.setItems(orderInfoItems);
        orderCreateVo.setPayPrice(orderInfo.getPayAmount());
        orderCreateVo.setFare(orderInfo.getFreightAmount());
        return orderCreateVo;
    }

    /**
     * 构建订单价格相关数据
     * @author 胡菜鸡
     * @date 2022/4/24 23:20
     */
    private OrderInfo computePrice(OrderInfo orderInfo, List<OrderInfoItem> orderInfoItems, OrderSubmitVo orderSubmitVo) {
        //设置订单总额
        BigDecimal totalPrice = new BigDecimal("0.0");
        BigDecimal promotion = new BigDecimal("0.0");
        Integer integration = new Integer(0);
        Integer growth = new Integer(0);
        for (OrderInfoItem orderInfoItem : orderInfoItems) {
            //总价格
            totalPrice = totalPrice.add(orderInfoItem.getRealAmount());
            //打折价
            promotion = promotion.add(orderInfoItem.getPromotionAmount());
            //积分
            integration += orderInfoItem.getGiftIntegration();
            //成长值
            growth += orderInfoItem.getGiftGrowth();
        }
        //设置总价格
        orderInfo.setTotalAmount(totalPrice);
        //设置积分和成长值
        orderInfo.setIntegration(integration);
        orderInfo.setGrowth(growth);
        //设置订单运费
        BigDecimal fare = orderPaymentInfoService.getFare(orderSubmitVo.getAddressId());
        orderInfo.setFreightAmount(fare);
        //设置优惠金额
        orderInfo.setPromotionAmount(promotion);
        if(!StringUtils.isBlank(orderSubmitVo.getCouponIdInput())){
            orderInfo.setCouponId(orderSubmitVo.getCouponIdInput());
            //这里应该去查询该id对应的优惠券，然后赋值
            orderInfo.setCouponAmount(new BigDecimal("0.00"));
        } else {
            //没有使用优惠券，直接赋值为 0
            orderInfo.setCouponAmount(new BigDecimal("0.00"));
        }
        orderInfo.setIntegrationAmount(orderSubmitVo.getIntegrationInput());
        //设置订单应付金额
        orderInfo.setPayAmount(orderInfo.getTotalAmount()
                .add(orderInfo.getFreightAmount())
                .subtract(orderInfo.getPromotionAmount())
                .subtract(orderInfo.getCouponAmount())
                .subtract(orderInfo.getIntegrationAmount()));
        //设置支付方式
        orderInfo.setPayType(orderSubmitVo.getPayType());
        return orderInfo;
    }

    /**
     * 构建基本订单数据
     * @author 胡菜鸡
     * @date 2022/4/24 22:15
     */
    private OrderInfo buildOrder() throws ServiceException {
        OrderInfo orderInfo = new OrderInfo();
        //1、生成一个订单号
        orderInfo.setOrderSn(IdUtils.simpleUUID());

        //2、获取订单对应的会员信息
        MemberInfoTo memberInfoTo = CartInterceptor.threadLocal.get();
        orderInfo.setMemberId(memberInfoTo.getId());
        orderInfo.setMemberUsername(memberInfoTo.getNickname());

        //3、获取并设置订单收货人信息
        OrderSubmitVo orderSubmitVo = orderThreadLocal.get();
        AjaxResult ajaxResult = memberFeignService.getAddressInfo(orderSubmitVo.getAddressId());
        MemberAddressVo memberAddressVo = ajaxResult.getData("data", new TypeReference<MemberAddressVo>() {});
        if(memberAddressVo.getProvince() == null){
            throw new ServiceException("用户收获地址为空", ExceptionTypeConstants.ORDER_SAVE);
        }
        orderInfo.setReceiverProvince(memberAddressVo.getProvince());
        orderInfo.setReceiverCity(memberAddressVo.getCity());
        orderInfo.setReceiverUniversity(memberAddressVo.getUniversity());
        orderInfo.setReceiverDetailAddress(memberAddressVo.getDetailAddress());
        orderInfo.setReceiverName(memberAddressVo.getName());
        orderInfo.setReceiverPhone(memberAddressVo.getPhone());

        //4、设置订单创建时间、订单状态、自动确认接收时间、删除状态
        orderInfo.setCreateTime(DateUtils.getNowDate());
        orderInfo.setStatus(0);
        orderInfo.setAutoConfirmDay(7);
        orderInfo.setDeleteStatus(0);
        return orderInfo;
    }

    /**
     * 构建订单项数据
     * @author 胡菜鸡
     * @date 2022/4/24 22:14
     */
    private List<OrderInfoItem> buildOrderItems(String orderSn) {
        List<CartItem> currentUserCartItems = cartController.getCurrentUserCartItems();
        if(currentUserCartItems != null && currentUserCartItems.size() > 0){
            List<OrderInfoItem> collect = currentUserCartItems.stream().map(cartItem -> {
                OrderInfoItem orderItem = new OrderInfoItem();
                //设置订单号
                orderItem.setOrderSn(orderSn);
                //设置书籍信息
                orderItem.setBookId(cartItem.getId());
                orderItem.setBookName(cartItem.getName());
                orderItem.setBookPic(cartItem.getImage());
                orderItem.setCategoryId(cartItem.getCategoryId());
                orderItem.setBookPrice(cartItem.getPrice());
                orderItem.setBookCount(cartItem.getCount());
                //设置价格信息
                orderItem.setPromotionAmount(new BigDecimal("0"));
                orderItem.setCouponAmount(new BigDecimal("0"));
                orderItem.setIntegrationAmount(new BigDecimal("0"));
                orderItem.setRealAmount(cartItem.getTotalPrice()
                        //商品项总价格减去每一项的优惠金额
                        .subtract(orderItem.getPromotionAmount())
                        .subtract(orderItem.getCouponAmount())
                        .subtract(orderItem.getIntegrationAmount()));
                //赠送成长值为 价格*10%
                orderItem.setGiftGrowth(orderItem.getRealAmount().divide(new BigDecimal(10)).intValue());
                //赠送积分为 价格*100%
                orderItem.setGiftIntegration(orderItem.getRealAmount().intValue());
                return orderItem;
            }).collect(Collectors.toList());
            return collect;
        }
        return null;
    }
}
